package org.springblade.tms.module.demandorder.base;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.logging.log4j.util.Strings;
import org.springblade.common.utils.EnumUtils;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.redis.lock.LockType;
import org.springblade.core.redis.lock.RedisLock;
import org.springblade.core.redis.lock.RedisLockClient;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.function.CheckedSupplier;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.tms.global.enums.*;
import org.springblade.tms.global.exception.CommonException;
import org.springblade.tms.global.utils.OrderNumUtil;
import org.springblade.tms.module.car.entity.TMCar;
import org.springblade.tms.module.car.service.ICarService;
import org.springblade.tms.module.carriercarrelation.entity.TMCarrierCarRelation;
import org.springblade.tms.module.carriercarrelation.service.ICarrierCarRelationService;
import org.springblade.tms.module.carrierdriverrelation.entity.TMCarrierDriverRelation;
import org.springblade.tms.module.carrierdriverrelation.service.ICarrierDriverRelationService;
import org.springblade.tms.module.carriersupercargorelation.entity.TMCarrierSupercargoRelation;
import org.springblade.tms.module.carriersupercargorelation.service.ICarrierSupercargoRelationService;
import org.springblade.tms.module.carriertankrelation.entity.TMCarrierTankRelation;
import org.springblade.tms.module.carriertankrelation.service.ICarrierTankRelationService;
import org.springblade.tms.module.demandextrainfo.entity.TmDemandExtraInfo;
import org.springblade.tms.module.demandextrainfo.service.ITmDemandExtraInfoService;
import org.springblade.tms.module.demandorder.dto.AllocatingWaybillDto;
import org.springblade.tms.module.demandorder.dto.DemandOrderDto;
import org.springblade.tms.module.demandorder.dto.Materials;
import org.springblade.tms.module.demandorder.entity.TMDemandOrder;
import org.springblade.tms.module.demandorder.service.IDemandOrderService;
import org.springblade.tms.module.demandorder.vo.DemandOrderVo;
import org.springblade.tms.module.demandorder.vo.DriverCarEscortVo;
import org.springblade.tms.module.demandorder.vo.TMDemandOrderVO;
import org.springblade.tms.module.demandorder.wrapper.DemandOrderWrapper;
import org.springblade.tms.module.demandorderitem.entity.TMDemandOrderItem;
import org.springblade.tms.module.demandorderitem.service.IDemandOrderItemService;
import org.springblade.tms.module.exceptionreason.entity.TMExceptionReason;
import org.springblade.tms.module.exceptionreason.service.IExceptionReasonService;
import org.springblade.tms.module.exceptionrecord.entity.TMExceptionRecord;
import org.springblade.tms.module.exceptionrecord.service.IExceptionRecordService;
import org.springblade.tms.module.exceptionrecord.vo.TMExceptionRecordVO;
import org.springblade.tms.module.exceptionrecord.wrapper.ExceptionRecordWrapper;
import org.springblade.tms.module.exceptiontype.entity.TMExceptionType;
import org.springblade.tms.module.exceptiontype.service.IExceptionTypeService;
import org.springblade.tms.module.gps.base.GPSRequestService;
import org.springblade.tms.module.gps.dto.GentlyBillGoodsDTO;
import org.springblade.tms.module.gps.dto.GentlyBillVehicleDTO;
import org.springblade.tms.module.gps.dto.WaybillSynchronizationDTO;
import org.springblade.tms.module.sap.base.SapRequestService;
import org.springblade.tms.module.sap.dto.SapTransportOrderDTO;
import org.springblade.tms.module.sap.dto.SapTransportOrderItemDTO;
import org.springblade.tms.module.sap.dto.SapTransportRespDTO;
import org.springblade.tms.module.sap.dto.WaybillEventDTO;
import org.springblade.tms.module.statementorder.entity.TMStatementOrder;
import org.springblade.tms.module.statementorder.service.IStatementOrderService;
import org.springblade.tms.module.supercargoinfo.entity.TMSupercargoInfo;
import org.springblade.tms.module.supercargoinfo.service.ISupercargoInfoService;
import org.springblade.tms.module.tank.entity.TMTankTank;
import org.springblade.tms.module.tank.service.ITankTankService;
import org.springblade.tms.module.transportlinepoint.entity.TMTransportLinePoint;
import org.springblade.tms.module.transportlinepoint.service.ITransportLinePointService;
import org.springblade.tms.module.transportorder.entity.TMTransportOrder;
import org.springblade.tms.module.transportorder.service.ITransportOrderService;
import org.springblade.tms.module.transportorder.vo.TMTransportOrderVO;
import org.springblade.tms.module.transportorder.wrapper.TransportOrderWrapper;
import org.springblade.tms.module.transportorderitem.entity.TMTransportOrderItem;
import org.springblade.tms.module.transportorderitem.service.ITransportOrderItemService;
import org.springblade.tms.module.userinfo.base.DriverManagementService;
import org.springblade.tms.module.userinfo.entity.TMUserInfo;
import org.springblade.tms.module.userinfo.service.IUserInfoService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static org.springblade.tms.global.constant.RedisConstant.DEMAND_ORDER_SERIAL_Lock;
import static org.springblade.tms.global.constant.RedisConstant.SHIPPING_ORDER_SERIAL_NUMBER;
import static org.springblade.tms.global.enums.WaybillStatusEnum.*;

/**
 * @projectName: BladeX
 * @package: org.springblade.tms.module.demandorder.base
 * @className: DemandListService
 * @author: yr
 * @description: 需求单服务
 * @date: 2022/3/16 17:02
 */
@Service
@Transactional
public class DemandListService {
    @Resource
    private IDemandOrderService demandOrderService;
    @Resource
    private IDemandOrderItemService demandOrderItemService;
    @Resource
    private DriverManagementService driverManagementService;
    @Resource
    private IUserInfoService userInfoService;
    @Resource
    private ISupercargoInfoService supercargoInfoService;
    @Resource
    private ICarService carService;
    @Resource
    private ICarrierCarRelationService carrierCarRelationService;
    @Resource
    private ICarrierDriverRelationService carrierDriverRelationService;
    @Resource
    private ICarrierSupercargoRelationService carrierSupercargoRelationService;
    @Resource
    private ITransportOrderService transportOrderService;
    @Resource
    private ITransportOrderItemService transportOrderItemService;
    @Resource
    private IExceptionRecordService exceptionRecordService;
    @Resource
    private ITransportLinePointService transportLinePointService;
    @Resource
    private SapRequestService sapRequestService;
    @Resource
    private IExceptionTypeService exceptionTypeService;
    @Resource
    private IExceptionReasonService exceptionReasonService;
    @Resource
    private GPSRequestService gpsRequestService;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private ITankTankService tankTankService;
    @Resource
    private ICarrierTankRelationService carrierTankRelationService;
    @Resource
    private ITmDemandExtraInfoService iTmDemandExtraInfoService;
    @Resource
    private IStatementOrderService statementOrderService;
    @Resource
    private RedisLockClient redisLockClient;
    //锁
    public static final String lock = "lock";

    /**
     * @param demandOrderDto:
     * @param query:
     * @return R<IPage < TMDemandOrderVO>>
     * @author yr
     * @description 需求单查询
     * @date 2022/3/16 17:16
     */
    public R<IPage<TMDemandOrderVO>> list(DemandOrderDto demandOrderDto, Query query) {
        String freightOrderTypes = demandOrderDto.getFreightOrderTypes();
        List<String> strings = new ArrayList<>();
        if (Strings.isNotBlank(freightOrderTypes)) {
            strings = Func.toStrList(freightOrderTypes);
        }
        TMUserInfo tmUserInfo = driverManagementService.getTmUserInfo();
        LambdaQueryWrapper<TMDemandOrder> queryWrapper = Condition.getQueryWrapper(new TMDemandOrder()).lambda()
                .eq(Strings.isNotBlank(demandOrderDto.getDemandOrderNumber()), TMDemandOrder::getDemandOrderNumber, demandOrderDto.getDemandOrderNumber())
                .eq(Strings.isNotBlank(demandOrderDto.getDemandStatus()), TMDemandOrder::getDemandStatus, demandOrderDto.getDemandStatus())
                .like(Strings.isNotBlank(demandOrderDto.getOrderNo()), TMDemandOrder::getOrderNo, demandOrderDto.getOrderNo())
                .like(Strings.isNotBlank(demandOrderDto.getOrderType()), TMDemandOrder::getOrderType, demandOrderDto.getOrderType())
                .like(Strings.isNotBlank(demandOrderDto.getDeliverNo()), TMDemandOrder::getDeliverNo, demandOrderDto.getDeliverNo())
                .like(Strings.isNotBlank(demandOrderDto.getStartLocationNo()), TMDemandOrder::getStartLocationNo, demandOrderDto.getStartLocationNo())
                .like(Strings.isNotBlank(demandOrderDto.getCarrierName()), TMDemandOrder::getCarrierName, demandOrderDto.getCarrierName())
                .like(Strings.isNotBlank(demandOrderDto.getTargetLocationNo()), TMDemandOrder::getTargetLocationNo, demandOrderDto.getTargetLocationNo())
                .in(Strings.isNotBlank(freightOrderTypes), TMDemandOrder::getOrderType, strings)
                .gt(demandOrderDto.getPlanGetGoodsDateStart() != null, TMDemandOrder::getPlanGetGoodsDateStart, demandOrderDto.getPlanGetGoodsDateStart())
                .lt(demandOrderDto.getPlanGetGoodsDateEnd() != null, TMDemandOrder::getPlanGetGoodsDateStart, demandOrderDto.getPlanGetGoodsDateEnd())
                .gt(demandOrderDto.getPlannedDeliveryDateStart() != null, TMDemandOrder::getPlanGetGoodsDateEnd, demandOrderDto.getPlannedDeliveryDateStart())
                .lt(demandOrderDto.getPlannedDeliveryDateEnd() != null, TMDemandOrder::getPlanGetGoodsDateEnd, demandOrderDto.getPlannedDeliveryDateEnd());
        if (tmUserInfo != null && tmUserInfo.getUserType().equals(UserTypeEnum.CARRIER.getValue())) {
            queryWrapper = queryWrapper.eq(TMDemandOrder::getCarrierNo, tmUserInfo.getTmId()).or().eq(TMDemandOrder::getFbsId,tmUserInfo.getId());
        }
        IPage<TMDemandOrder> page = demandOrderService.page(Condition.getPage(query), queryWrapper.orderByDesc(TMDemandOrder::getCreateTime, TMDemandOrder::getId));
        IPage<TMDemandOrderVO> tmDemandOrderVOIPage = DemandOrderWrapper.build().pageVO(page);
        List<Long> ids = page.getRecords().stream().map(TMDemandOrder::getId).collect(Collectors.toList());
        List<TMDemandOrderItem> tmDemandOrderItems = demandOrderItemService.lambdaQuery()
                .in(!ids.isEmpty(), TMDemandOrderItem::getParentId, ids).apply(ids.isEmpty(), "1=2").list();
        Map<Long, List<TMDemandOrderItem>> transportMap = tmDemandOrderItems.stream().collect(Collectors.groupingBy(TMDemandOrderItem::getParentId));
        for (TMDemandOrderVO record : tmDemandOrderVOIPage.getRecords()) {
            record.setIsItPossibleToAssignAWaybill(!transportMap.getOrDefault(record.getId(), new ArrayList<>()).stream().allMatch(item -> BigDecimal.ZERO.compareTo(item.getNum()) == 0));
            record.setSourceLocationStreet(new StringBuilder(record.getStartLocationArea())
                    .append(record.getStartLocationCity()).append(record.getStartLocationStreet()).toString());
            record.setDestinationStreet(new StringBuilder(record.getTargetLocationArea())
                    .append(record.getTargetLocationCity()).append(record.getTargetLocationStreet()).toString());
            transportMap.get(record.getId()).forEach(item -> {
                record.setMaterialInformation(new StringBuilder(record.getMaterialInformation()).append(item.getMatDesc())
                        .append("*").append(item.getNum().toString()).append("(")
                        .append(item.getUnit()).append(")").toString());
            });
        }
        return R.data(tmDemandOrderVOIPage);
    }

    /**
     * @param id:
     * @param query:
     * @return R<DemandOrderVo>
     * @author yr
     * @description 需求单详情接口
     * @date 2022/3/17 11:01
     */
    public R<DemandOrderVo> detail(Long id, Query query) {
        DemandOrderVo demandOrderVo = new DemandOrderVo();
        TMDemandOrder tmDemandOrder = demandOrderService.lambdaQuery().eq(TMDemandOrder::getId, id).one();
        demandOrderVo.setDemandOrder(tmDemandOrder);
        IPage<TMDemandOrderItem> page = demandOrderItemService.page(Condition.getPage(query)
                , Condition.getQueryWrapper(new TMDemandOrderItem()).lambda().eq(TMDemandOrderItem::getParentId, id));
        demandOrderVo.setDemandOrderItemList(page.getRecords());
        List<TMExceptionRecord> exceptionRecordList = exceptionRecordService.lambdaQuery().eq(TMExceptionRecord::getParentId, id).list();
        List<TMExceptionRecordVO> tmExceptionRecordVOS = ExceptionRecordWrapper.build().listVO(exceptionRecordList);
        List<Long> exceptionTypeList = tmExceptionRecordVOS.stream().map(TMExceptionRecord::getExceptionType).collect(Collectors.toList());
        List<Long> exceptionTypeDescList = tmExceptionRecordVOS.stream().map(TMExceptionRecord::getExceptionTypeDesc).collect(Collectors.toList());
        Map<Long, String> exceptionTypeMap = exceptionTypeService.lambdaQuery().in(!exceptionTypeList.isEmpty(), TMExceptionType::getId, exceptionTypeList).list()
                .stream().collect(Collectors.toMap(TMExceptionType::getId, TMExceptionType::getName));
        Map<Long, String> exceptionTypeDescMap = exceptionReasonService.lambdaQuery().in(!exceptionTypeDescList.isEmpty(), TMExceptionReason::getId, exceptionTypeDescList).list()
                .stream().collect(Collectors.toMap(TMExceptionReason::getId, TMExceptionReason::getReasonDesc));
        tmExceptionRecordVOS.forEach(tmExceptionRecord -> {
            tmExceptionRecord.setExceptionClass(exceptionTypeMap.get(tmExceptionRecord.getExceptionType()));
            tmExceptionRecord.setAbnormal(exceptionTypeDescMap.get(tmExceptionRecord.getExceptionTypeDesc()));
        });
        demandOrderVo.setExceptionRecord(tmExceptionRecordVOS);
        return R.data(demandOrderVo);
    }



	public static void filterTankTankPage(IPage<TMTankTank> tmTankTankIPage, IPage<TMDemandOrderItem> orderItemIPage) {
		List<TMTankTank> tankTankList = tmTankTankIPage.getRecords();
		List<TMDemandOrderItem> orderItemList = orderItemIPage.getRecords();
		List<TMTankTank> toRemove = new ArrayList<>();

		for (TMTankTank tankTank : tankTankList) {
//			boolean found = false;
			for (TMDemandOrderItem orderItem : orderItemList) {
				if (!orderItem.getMatDesc().contains(tankTank.getProduct())) {
					toRemove.add(tankTank);
//					break;
				}
			}
//			if (!found) {
//			}
		}

		tankTankList.removeAll(toRemove);
		tmTankTankIPage.setRecords(tankTankList);
	}

    /**
     * @param tmId:
     * @param query:
     * @return R<DriverCarDto>
     * @author yr
     * @description 司机车辆押运员列表
     * @date 2022/3/17 16:12
     */
    public R<Object> driverCarEscortList(String tmId, Long id, Long transportId, Query query) {
        query = new Query();
        query.setSize(-1);
        query.setCurrent(-1);
        TMUserInfo tmUserInfo = userInfoService.lambdaQuery().eq(TMUserInfo::getTmId, tmId).one();
        //查询司机车辆押运员坦克罐
        List<TMCarrierCarRelation> tmCarrierCarRelations = carrierCarRelationService.lambdaQuery()
                .eq(TMCarrierCarRelation::getRelationCarrierNo, tmUserInfo.getId()).list();
        List<TMCarrierTankRelation> tmCarrierTankRelations = carrierTankRelationService.lambdaQuery()
                .eq(TMCarrierTankRelation::getRelationCarrierNo, tmUserInfo.getId()).list();
        List<TMCarrierDriverRelation> tmCarrierDriverRelations = carrierDriverRelationService.lambdaQuery()
                .eq(TMCarrierDriverRelation::getRelationCarrierNo, tmUserInfo.getId()).list();
        List<TMCarrierSupercargoRelation> tmCarrierSupercargoRelations = carrierSupercargoRelationService.lambdaQuery()
                .eq(TMCarrierSupercargoRelation::getRelationCarrierNo, tmUserInfo.getId()).list();
        List<Long> carIds = tmCarrierCarRelations.stream().map(TMCarrierCarRelation::getRelationCarNo).collect(Collectors.toList());
        List<Long> tankIds = tmCarrierTankRelations.stream().map(TMCarrierTankRelation::getRelationTankNo).collect(Collectors.toList());
        List<Long> driverIds = tmCarrierDriverRelations.stream().map(TMCarrierDriverRelation::getRelationDriver).collect(Collectors.toList());
        List<Long> supercargoIds = tmCarrierSupercargoRelations.stream().map(TMCarrierSupercargoRelation::getRelationSupercargoNo).collect(Collectors.toList());
        TMTransportOrder transportOrder = transportOrderService.getById(transportId);
        if (transportOrder == null) {
            transportOrder = new TMTransportOrder();
        }
        //司机查询
        IPage<TMUserInfo> userInfoIPage = userInfoService.page(Condition.getPage(query), Condition.getQueryWrapper(new TMUserInfo()).lambda()
                .eq(TMUserInfo::getApprovalStatus, ApprovalStatusEnum.PASSED.getValue())
                .in(!driverIds.isEmpty(), TMUserInfo::getId, driverIds).apply(driverIds.isEmpty(), "1=2")
                .or().eq(transportOrder.getDriverId() != null, TMUserInfo::getId, transportOrder.getDriverId())
                .orderByDesc(TMUserInfo::getUpdateTime, TMUserInfo::getId));
        //车辆查询
        IPage<TMCar> carIPage = carService.page(Condition.getPage(query), Condition.getQueryWrapper(new TMCar()).lambda()
                .eq(TMCar::getApprovalStatus, ApprovalStatusEnum.PASSED.getValue())
                .in(!carIds.isEmpty(), TMCar::getId, carIds).apply(carIds.isEmpty(), "1=2")
                .and(tmCarLambdaQueryWrapper -> tmCarLambdaQueryWrapper.eq(TMCar::getCarType, CarType.TRACTOR.getValue())
                        .or().eq(TMCar::getCarType, CarType.VEHICLE.getValue()))
                .or().eq(transportOrder.getCarNo() != null, TMCar::getCarNo, transportOrder.getCarNo())
                .orderByDesc(TMCar::getUpdateTime, TMCar::getId));
        //挂车查询
        IPage<TMCar> trailer = carService.page(Condition.getPage(query), Condition.getQueryWrapper(new TMCar()).lambda()
                .eq(TMCar::getApprovalStatus, ApprovalStatusEnum.PASSED.getValue())
                .in(!carIds.isEmpty(), TMCar::getId, carIds).apply(carIds.isEmpty(), "1=2")
                .eq(TMCar::getCarType, CarType.TRAILER.getValue())
                .or().eq(Strings.isNotBlank(transportOrder.getBindCarNo()), TMCar::getBindCarNo, transportOrder.getBindCarNo())
                .orderByDesc(TMCar::getUpdateTime, TMCar::getId));
        //押运员查询
        IPage<TMSupercargoInfo> supercargoInfoIPage = supercargoInfoService.page(Condition.getPage(query), Condition.getQueryWrapper(new TMSupercargoInfo()).lambda()
                .eq(TMSupercargoInfo::getApprovalStatus, ApprovalStatusEnum.PASSED.getValue())
                .in(!supercargoIds.isEmpty(), TMSupercargoInfo::getId, supercargoIds).apply(supercargoIds.isEmpty(), "1=2")
                .or().eq(transportOrder.getSupercargoId() != null, TMSupercargoInfo::getId, transportOrder.getSupercargoId())
                .orderByDesc(TMSupercargoInfo::getUpdateTime, TMSupercargoInfo::getId));
        //坦克罐查询
        IPage<TMTankTank> tmTankTankIPage = tankTankService.page(Condition.getPage(query), Condition.getQueryWrapper(new TMTankTank()).lambda()
                .in(!tankIds.isEmpty(), TMTankTank::getId, tankIds).apply(tankIds.isEmpty(), "1=2")
                .or().eq(transportOrder.getContainerNo() != null, TMTankTank::getId, transportOrder.getContainerNo())
                .orderByDesc(TMTankTank::getUpdateTime, TMTankTank::getId));
        //需求单行项目查询
        IPage<TMDemandOrderItem> orderItemIPage = demandOrderItemService.page(Condition.getPage(query), Condition.getQueryWrapper(new TMDemandOrderItem()).lambda()
                .eq(TMDemandOrderItem::getParentId, id).orderByDesc(TMDemandOrderItem::getUpdateTime, TMDemandOrderItem::getId));
        orderItemIPage.getRecords().forEach(tmDemandOrderItem -> tmDemandOrderItem.setNum(tmDemandOrderItem.getNum().setScale(4, BigDecimal.ROUND_DOWN)));

		// 过滤操作
		filterTankTankPage(tmTankTankIPage, orderItemIPage);

        //运输单行项目查询
        List<TMTransportOrderItem> tmTransportOrderItems = transportOrderItemService.lambdaQuery().eq(TMTransportOrderItem::getParentId, transportId).orderByDesc(TMTransportOrderItem::getUpdateTime, TMTransportOrderItem::getId).list();
        //运输单详情查询
        TMTransportOrderVO tmTransportOrderVO = null;
        if (transportId != null) {
            TMUserInfo userInfo = userInfoService.getById(transportOrder.getDriverId());
            tmTransportOrderVO = TransportOrderWrapper.build().entityVO(transportOrder);
            TMCar tmCar = carService.lambdaQuery().eq(TMCar::getCarNo, transportOrder.getCarNo()).one();
            if (tmCar != null) {
                tmTransportOrderVO.setCarId(tmCar.getId());
            }
            if (userInfo != null) {
                tmTransportOrderVO.setDriverPhone(userInfo.getPhone());
            } else {
                tmTransportOrderVO.setDriverId(null);
            }
        }
        //将司机车辆押运员封装到vo
        DriverCarEscortVo driverCarEscortVo = new DriverCarEscortVo();
        driverCarEscortVo.setCar(carIPage.getRecords());
        driverCarEscortVo.setTank(tmTankTankIPage.getRecords());
        driverCarEscortVo.setTrailer(trailer.getRecords());
        driverCarEscortVo.setDriver(userInfoIPage.getRecords());
        driverCarEscortVo.setSupercargoInfo(supercargoInfoIPage.getRecords());
        driverCarEscortVo.setDemandOrderItems(orderItemIPage.getRecords());
        driverCarEscortVo.setTransportOrderItems(tmTransportOrderItems);
        driverCarEscortVo.setTransportOrder(tmTransportOrderVO);
        return R.data(JSON.toJSON(driverCarEscortVo));
    }

    /**
     * @param allocatingWaybillDto:
     * @return R
     * @author yr
     * @description 生成运单
     * @date 2022/3/17 17:16
     */
    @RedisLock(DEMAND_ORDER_SERIAL_Lock + "allocatingWaybill")
    public R<Long> allocatingWaybill(AllocatingWaybillDto allocatingWaybillDto) {
        R<Long> r = new R<>();
        //判断坦克罐是否在使用
        if (Strings.isNotBlank(allocatingWaybillDto.getTankType()) && allocatingWaybillDto.getTankType().equals(TankTypeEnum.TANK_TANK.getValue())) {
            TMTransportOrder tmTransportOrder = transportOrderService.lambdaQuery()
                    .eq(TMTransportOrder::getContainerNo, allocatingWaybillDto.getContainerNo())
                    .ne(TMTransportOrder::getId, allocatingWaybillDto.getTransportId()).one();
            if (tmTransportOrder != null) {
                CommonException.throwCommonException("坦克罐正在使用！");
            }
        }
        //生成运单
        TMCar car = carService.lambdaQuery().eq(TMCar::getId, allocatingWaybillDto.getCarId()).one();
        TMUserInfo driver = userInfoService.lambdaQuery().eq(TMUserInfo::getId, allocatingWaybillDto.getDriverId()).one();
        TMSupercargoInfo supercargoInfo = supercargoInfoService.lambdaQuery().eq(TMSupercargoInfo::getId, allocatingWaybillDto.getEscortId()).one();
        if (supercargoInfo == null) {
            supercargoInfo = new TMSupercargoInfo();
        }
        TMDemandOrder demandOrder = demandOrderService.lambdaQuery().eq(TMDemandOrder::getId, allocatingWaybillDto.getRequestId()).one();
        //校验司机车辆押运员承运商是否过期
        TMUserInfo userInfo = userInfoService.lambdaQuery().eq(TMUserInfo::getId, demandOrder.getCarrierId())
                .and(tmUserInfoLambdaQueryWrapper -> tmUserInfoLambdaQueryWrapper
                        .le(TMUserInfo::getCertificateTransportValidDateEnd, LocalDateTime.now()).or()
                        .le(TMUserInfo::getCertificateOperationValidDateEnd, LocalDateTime.now())).one();
        if (userInfo != null) {
            CommonException.throwCommonException("您的资质已经过期");
        }
        TMCar tmCar = carService.lambdaQuery().eq(TMCar::getId, allocatingWaybillDto.getCarId())
                .and(tmUserInfoLambdaQueryWrapper -> tmUserInfoLambdaQueryWrapper
                        .ne(TMCar::getCertificationBindCarCheckValidEnd, LocalDateTime.now()).or()
                        .ne(TMCar::getCertificationCarCheckValidEnd, LocalDateTime.now()).or()
                        .ne(TMCar::getCertificationCarWorkValidEnd, LocalDateTime.now()).or()
                        .ne(TMCar::getCertificationBindCarWorkValidEnd, LocalDateTime.now()).or()
                        .ne(TMCar::getContainerReportValidEnd, LocalDateTime.now()).or()
                        .ne(TMCar::getCertificateTankTruckEnd, LocalDateTime.now()).or()
                        .ne(TMCar::getVehicleInsuranceValidEnd, LocalDateTime.now())).one();
        if (tmCar == null) {
            CommonException.throwCommonException("所选车辆资质已经过期");
        }
        TMSupercargoInfo tmSupercargoInfo = supercargoInfoService.lambdaQuery().eq(TMSupercargoInfo::getId, allocatingWaybillDto.getEscortId())
                .and(supercargoInfoLambdaQueryWrapper -> supercargoInfoLambdaQueryWrapper
                        .le(TMSupercargoInfo::getValidityPeriod, LocalDateTime.now()).or()
                        .le(TMSupercargoInfo::getIdCardValidEnd, LocalDateTime.now())).one();
        if (tmSupercargoInfo != null) {
            CommonException.throwCommonException("所选押运员资质已经过期");
        }
        TMUserInfo tmDriver = userInfoService.lambdaQuery().eq(TMUserInfo::getId, allocatingWaybillDto.getDriverId())
                .and(tmUserInfoLambdaQueryWrapper -> tmUserInfoLambdaQueryWrapper
                        .le(TMUserInfo::getIdCardValidEnd, LocalDateTime.now()).or()
                        .le(TMUserInfo::getCertificateDriverValidEnd, LocalDateTime.now())).one();
        if (tmDriver != null) {
            CommonException.throwCommonException("所选司机资质已经过期");
        }
        if (!tmCar.getCarType().equals(CarType.VEHICLE.getValue())) {
            TMCar bindCar = carService.lambdaQuery().eq(TMCar::getBindCarNo, allocatingWaybillDto.getBindCarNo())
                    .eq(TMCar::getCarType, CarType.TRAILER.getValue())
                    .and(tmUserInfoLambdaQueryWrapper -> tmUserInfoLambdaQueryWrapper
                            .le(TMCar::getCertificationBindCarCheckValidEnd, LocalDateTime.now()).or()
                            .le(TMCar::getCertificationCarCheckValidEnd, LocalDateTime.now()).or()
                            .le(TMCar::getCertificationCarWorkValidEnd, LocalDateTime.now()).or()
                            .le(TMCar::getCertificationBindCarWorkValidEnd, LocalDateTime.now()).or()
                            .le(TMCar::getContainerReportValidEnd, LocalDateTime.now()).or()
                            .le(TMCar::getCertificateTankTruckEnd, LocalDateTime.now()).or()
                            .le(TMCar::getVehicleInsuranceValidEnd, LocalDateTime.now())).one();
            if (bindCar != null) {
                CommonException.throwCommonException("所选挂车资质已经过期");
            }
        }
        TMTransportOrder transportOrder = getTransportOrder(car, driver, supercargoInfo, demandOrder, allocatingWaybillDto.getBindCarNo(), allocatingWaybillDto.getContainerNo(), allocatingWaybillDto.getLeadSealNumber());
        transportOrder.setExpectedArrivalTimeAtTheFactory(allocatingWaybillDto.getExpectedArrivalTimeAtTheFactory());
        //司机车辆押运员校验
        //校验是否可以拼单
        List<TMTransportOrder> tmTransportOrders = transportOrderService.lambdaQuery()
                .eq(TMTransportOrder::getCarNo, car.getCarNo())
                .eq(allocatingWaybillDto.getBindCarNo() != null, TMTransportOrder::getBindCarNo, allocatingWaybillDto.getBindCarNo())
                .eq(TMTransportOrder::getDriverId, driver.getId())
                .eq(supercargoInfo.getId() != null, TMTransportOrder::getSupercargoId, supercargoInfo.getId())
                .and(queryWrapper -> queryWrapper
                        .and(tmTransportOrderLambdaQueryWrapper -> tmTransportOrderLambdaQueryWrapper
                                .eq(TMTransportOrder::getTransportStatus, SHIPPED.getValue())
                                .eq(TMTransportOrder::getTransportStatus, ARRIVED.getValue()))
                        .and(wrapper -> wrapper.ne(TMTransportOrder::getOrderType, TorTypeEnum.ZFO4.getValue())
                                .or().ne(TMTransportOrder::getOrderType, TorTypeEnum.ZFOA.getValue()))
                        .or(wrapper -> wrapper.eq(TMTransportOrder::getTransportStatus, ARRIVED.getValue())
                        ))
                .eq(TMTransportOrder::getAbnormalSpelling, WhetherEnum.no.getValue())
                .list();
        if (!tmTransportOrders.isEmpty()) {
            throw CommonException.throwCommonException("当前车辆正在使用！");
        } else {
            if (!allocatingWaybillDto.getWhetherToMakeASingle()) {
                List<TMTransportOrder> list = transportOrderService.lambdaQuery()
                        .eq(TMTransportOrder::getCarNo, car.getCarNo())
                        .eq(allocatingWaybillDto.getBindCarNo() != null, TMTransportOrder::getBindCarNo, allocatingWaybillDto.getBindCarNo())
                        .eq(TMTransportOrder::getDriverId, driver.getId())
                        .eq(supercargoInfo.getId() != null, TMTransportOrder::getSupercargoId, supercargoInfo.getId())
                        .and(wrapper -> wrapper.eq(TMTransportOrder::getTransportStatus, LOADED.getValue())
                                .or().eq(TMTransportOrder::getTransportStatus, WaybillStatusEnum.ORDER_RECEIVED.getValue())
                                .or().eq(TMTransportOrder::getAbnormalSpelling, WhetherEnum.yes.getValue()))
                        .list();
                if (!list.isEmpty()) {
                    r.setCode(206);
                    r.setSuccess(true);
                    r.setMsg("该车辆已使用，是否进行拼单!");
                }
                //验证车辆司机押运员是否正在使用
                //判断车辆
                List<TMTransportOrder> tmTransportOrders1 = transportOrderService.lambdaQuery()
                        .eq(TMTransportOrder::getCarNo, car.getCarNo())
                        .and(tmTransportOrderLambdaQueryWrapper -> tmTransportOrderLambdaQueryWrapper
                                .ne(TMTransportOrder::getTransportStatus, WaybillStatusEnum.COMPLETED.getValue())
                                .ne(TMTransportOrder::getTransportStatus, WaybillStatusEnum.ABOLISHED.getValue())
                                .ne(r.getCode() != 206, TMTransportOrder::getTransportStatus, LOADED.getValue())
                                .and(wrapper -> wrapper
                                        .or(orderType -> orderType.eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFO4.getValue())
                                                .or().eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFOA.getValue())
                                                .or().eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFOB.getValue())
                                                .or().eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFO5.getValue())
                                                .and(tmTransportOrderLambdaQueryWrapper1 -> tmTransportOrderLambdaQueryWrapper1
                                                        .ne(TMTransportOrder::getTransportStatus, SHIPPED.getValue())
                                                        .ne(TMTransportOrder::getTransportStatus, ARRIVED.getValue()))
                                        )))
                        .eq(TMTransportOrder::getAbnormalSpelling, WhetherEnum.no.getValue())
                        .list();
                //判断挂车
                List<TMTransportOrder> tmTransportOrders2 = transportOrderService.lambdaQuery()
                        .eq(TMTransportOrder::getBindCarNo, allocatingWaybillDto.getBindCarNo())
                        .and(tmTransportOrderLambdaQueryWrapper -> tmTransportOrderLambdaQueryWrapper
                                .ne(TMTransportOrder::getTransportStatus, WaybillStatusEnum.COMPLETED.getValue())
                                .ne(TMTransportOrder::getTransportStatus, WaybillStatusEnum.ABOLISHED.getValue())
                                .ne(r.getCode() != 206, TMTransportOrder::getTransportStatus, LOADED.getValue())
                                .and(wrapper -> wrapper
                                        .or(orderType -> orderType.eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFO4.getValue())
                                                .or().eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFOA.getValue())
                                                .or().eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFOB.getValue())
                                                .or().eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFO5.getValue())
                                                .and(tmTransportOrderLambdaQueryWrapper1 -> tmTransportOrderLambdaQueryWrapper1
                                                        .ne(TMTransportOrder::getTransportStatus, SHIPPED.getValue())
                                                        .ne(TMTransportOrder::getTransportStatus, ARRIVED.getValue()))
                                        )))
                        .eq(TMTransportOrder::getAbnormalSpelling, WhetherEnum.no.getValue())
                        .apply(Strings.isBlank(allocatingWaybillDto.getBindCarNo()), "1=2")
                        .list();
                //判断司机
                List<TMTransportOrder> tmTransportOrders3 = transportOrderService.lambdaQuery()
                        .eq(TMTransportOrder::getDriverId, driver.getId())
                        .and(tmTransportOrderLambdaQueryWrapper -> tmTransportOrderLambdaQueryWrapper
                                .ne(TMTransportOrder::getTransportStatus, WaybillStatusEnum.COMPLETED.getValue())
                                .ne(TMTransportOrder::getTransportStatus, WaybillStatusEnum.ABOLISHED.getValue())
                                .ne(r.getCode() != 206, TMTransportOrder::getTransportStatus, LOADED.getValue())
                                .and(wrapper -> wrapper
                                        .or(orderType -> orderType.eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFO4.getValue())
                                                .or().eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFOA.getValue())
                                                .or().eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFOB.getValue())
                                                .or().eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFO5.getValue())
                                                .and(tmTransportOrderLambdaQueryWrapper1 -> tmTransportOrderLambdaQueryWrapper1
                                                        .ne(TMTransportOrder::getTransportStatus, SHIPPED.getValue())
                                                        .ne(TMTransportOrder::getTransportStatus, ARRIVED.getValue()))
                                        )))
                        .eq(TMTransportOrder::getAbnormalSpelling, WhetherEnum.no.getValue())
                        .list();
                //判断押运员
                List<TMTransportOrder> tmTransportOrders4 = transportOrderService.lambdaQuery()
                        .eq(TMTransportOrder::getSupercargoId, supercargoInfo.getId())
                        .and(tmTransportOrderLambdaQueryWrapper -> tmTransportOrderLambdaQueryWrapper
                                .ne(TMTransportOrder::getTransportStatus, WaybillStatusEnum.COMPLETED.getValue())
                                .ne(TMTransportOrder::getTransportStatus, WaybillStatusEnum.ABOLISHED.getValue())
                                .ne(r.getCode() != 206, TMTransportOrder::getTransportStatus, LOADED.getValue())
                                .and(wrapper -> wrapper
                                        .or(orderType -> orderType.eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFO4.getValue())
                                                .or().eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFOA.getValue())
                                                .or().eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFOB.getValue())
                                                .or().eq(TMTransportOrder::getOrderType, TorTypeEnum.ZFO5.getValue())
                                                .and(tmTransportOrderLambdaQueryWrapper1 -> tmTransportOrderLambdaQueryWrapper1
                                                        .ne(TMTransportOrder::getTransportStatus, SHIPPED.getValue())
                                                        .ne(TMTransportOrder::getTransportStatus, ARRIVED.getValue()))
                                        )))
                        .eq(TMTransportOrder::getAbnormalSpelling, WhetherEnum.no.getValue())
                        .apply(supercargoInfo.getId() == null, "1=2")
                        .list();
                if (!tmTransportOrders1.isEmpty()) {
                    throw CommonException.throwCommonException("当前车辆正在使用！");
                } else if (!tmTransportOrders2.isEmpty()) {
                    throw CommonException.throwCommonException("当前挂车正在使用！");
                } else if (!tmTransportOrders3.isEmpty()) {
                    throw CommonException.throwCommonException("当前司机正在使用！");
                } else if (!tmTransportOrders4.isEmpty()) {
                    throw CommonException.throwCommonException("当前押运员正在使用！");
                }
            }
        }
        if (r.getCode() == 206) {
            return r;
        }
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        Date date = new Date();
        if (!redisTemplate.hasKey(SHIPPING_ORDER_SERIAL_NUMBER)) {
            redisTemplate.opsForValue().set(SHIPPING_ORDER_SERIAL_NUMBER, "0001");
        }

        String serialNumber = redisTemplate.opsForValue().get(SHIPPING_ORDER_SERIAL_NUMBER).toString();
        try {
            Boolean lockSuccess =  redisLockClient.tryLock("YDS_into", LockType.REENTRANT, 110, 100, TimeUnit.SECONDS);
            if(!lockSuccess){
                CommonException.throwCommonException("get redis lock failed");
            }
            transportOrder.setTransportNo(new StringBuilder(EnumUtils.getEnumByValue(WerksCodeEnum.class, demandOrder.getWerks()).get().getDescription())
                    .append(WerksCodeEnum.YSD.getValue()).append(format.format(date).replace("-", ""))
                    .append(serialNumber).toString());
            transportOrder.setTankType(allocatingWaybillDto.getTankType());
            transportOrderService.save(transportOrder);
            String addNum = OrderNumUtil.addNum(serialNumber);
            redisTemplate.opsForValue().getAndSet(SHIPPING_ORDER_SERIAL_NUMBER, addNum);

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            redisLockClient.unLock("YDS_into", LockType.REENTRANT);
        }
        //获取物料id
        List<Long> ids = allocatingWaybillDto.getMaterials().stream().map(Materials::getId).collect(Collectors.toList());
        List<TMDemandOrderItem> demandOrderItems = demandOrderItemService.lambdaQuery().in(TMDemandOrderItem::getId, ids).list();
        List<TMTransportOrderItem> orderItemList = new ArrayList<>();
        for (TMDemandOrderItem demandOrderItem : demandOrderItems) {
            for (Materials material : allocatingWaybillDto.getMaterials()) {
                if (demandOrderItem.getId().equals(material.getId())) {
                    BigDecimal decimal = demandOrderItem.getNum().subtract(material.getNum());
                    if (decimal.intValue() < 0) {
                        throw CommonException.throwCommonException("物料数量不足！");
                    }
                    demandOrderItem.setNum(decimal);
                    //映射运单行项目
                    TMTransportOrderItem item = BeanUtil.copy(demandOrderItem, TMTransportOrderItem.class);
                    item.setParentId(transportOrder.getId());
                    item.setNum(material.getNum().setScale(4, BigDecimal.ROUND_DOWN));
                    item.setId(null);
                    orderItemList.add(item);
                }
            }
        }
        //生成运单日志
        TMTransportLinePoint tmTransportLinePoint = new TMTransportLinePoint();
        tmTransportLinePoint.setParentId(transportOrder.getId());
        tmTransportLinePoint.setTransport(WaybillStatusEnum.ORDER_RECEIVED.getDescription());
        tmTransportLinePoint.setCreateTime(new Date());
        transportLinePointService.save(tmTransportLinePoint);
        demandOrderItemService.updateBatchById(demandOrderItems);
        transportOrderItemService.saveBatch(orderItemList);
        //修改需求单状态
        demandOrder.setDemandStatus(DemandStatusEnum.ALREADY.value());
        demandOrderService.updateById(demandOrder);
        //上报gps
        if (transportOrder.getPushTag().equals("1")) {
            WaybillSynchronizationDTO waybillSynchronizationDTO = BeanUtil.copy(transportOrder, WaybillSynchronizationDTO.class);
            List<GentlyBillGoodsDTO> billGoodsDTOList = BeanUtil.copy(orderItemList, GentlyBillGoodsDTO.class);
            waybillSynchronizationDTO.setGentlyBillGoods(billGoodsDTOList.get(0));
            GentlyBillVehicleDTO gentlyBillVehicleDTO = BeanUtil.copy(car, GentlyBillVehicleDTO.class);
            BeanUtil.copyNonNull(driver, gentlyBillVehicleDTO);
            gentlyBillVehicleDTO.setDeviceNo(car.getGpsEquipmentNumber());
            gentlyBillVehicleDTO.setDeviceType(car.getTransportGoodsType().equals(CargoTypeEnum.DANGEROUS_GOODS.getValue()) ? "1" : "2");
            waybillSynchronizationDTO.setGentlyBillVehicle(gentlyBillVehicleDTO);
            gpsRequestService.waybillSynchronization(waybillSynchronizationDTO);
        }
//		上报sap
        SapTransportOrderDTO sapTransportOrderDTO = SapTransportOrderDTO.builder().build();
        BeanUtil.copyNonNull(transportOrder, sapTransportOrderDTO);
        sapTransportOrderDTO.setLeadSealNumber(allocatingWaybillDto.getLeadSealNumber());
        sapTransportOrderDTO.setDriverPhone(driver.getPhone());
        sapTransportOrderDTO.setSupercargoPhone(supercargoInfo.getPhone());
        sapTransportOrderDTO.setIdCardNo(supercargoInfo.getIdCardNo());
        sapTransportOrderDTO.setCertificateDriverNo(driver.getCertificateDriverNo());
        List<SapTransportOrderItemDTO> sapTransportOrderItemDTOS = new ArrayList<>();
        for (TMTransportOrderItem tmTransportOrderItem : orderItemList) {
            sapTransportOrderItemDTOS.add(BeanUtil.copy(tmTransportOrderItem, SapTransportOrderItemDTO.class));
        }
        sapTransportOrderDTO.setType(SapTransportActionEnum.TRANSPORT.value());
        sapTransportOrderDTO.setTransportNo(transportOrder.getTransportNo());
        sapTransportOrderDTO.setItem(sapTransportOrderItemDTOS);
        SapTransportRespDTO sapTransportRespDTO = sapRequestService.pushTmForDemandExtraInfo(sapTransportOrderDTO);
        transportOrder.setOrderNoNew(sapTransportRespDTO.getOrderNoNew().toString());
        transportOrder.setDeliverNoNew(sapTransportRespDTO.getDeliverNoNew().toString());
        transportOrderService.updateById(transportOrder);
        if (Strings.isNotBlank(demandOrder.getBillOfLadingNumber())) {
            iTmDemandExtraInfoService.save(TmDemandExtraInfo.builder()
                    .parentId(transportOrder.getId())
                    .deliverNo(demandOrder.getBillOfLadingNumber())
                    .build());
        }
        return R.data(transportOrder.getId());
    }

    private TMTransportOrder getTransportOrder(TMCar car, TMUserInfo driver, TMSupercargoInfo supercargoInfo, TMDemandOrder demandOrder, String bindCarNo, String containerNo, String leadSealNumber) {
        TMTransportOrder tmTransportOrder = new TMTransportOrder();
        BeanUtil.copy(demandOrder, tmTransportOrder);
        tmTransportOrder.setDriverId(driver.getId());
        tmTransportOrder.setDriverName(driver.getName());
        tmTransportOrder.setCarNo(car.getCarNo());
        tmTransportOrder.setPushTag(car.getEnableGps().toString());
        tmTransportOrder.setBindCarNo(bindCarNo);
        tmTransportOrder.setLeadSealNumber(leadSealNumber);
        tmTransportOrder.setTransportStatus(WaybillStatusEnum.ORDER_RECEIVED.getValue());
        tmTransportOrder.setContainerNo(containerNo);
        if (supercargoInfo != null) {
            tmTransportOrder.setSupercargoId(supercargoInfo.getId());
            tmTransportOrder.setSupercargoName(supercargoInfo.getName());
        }
        tmTransportOrder.setDemandId(demandOrder.getId());
        tmTransportOrder.setId(null);
        return tmTransportOrder;
    }

    /**
     * @param id:
     * @return R
     * @author yr
     * @description 需求单作废
     * @date 2022/8/22 14:46
     */
    public R invalid(Long id, String reasonForVoiding) {
        List<TMTransportOrder> tmTransportOrders = transportOrderService.lambdaQuery()
                .eq(TMTransportOrder::getDemandId, id)
                .ne(TMTransportOrder::getTransportStatus, WaybillStatusEnum.ABOLISHED.getValue())
                .list();
        if (!tmTransportOrders.isEmpty()) {
            CommonException.throwCommonException("需求单下存在有效运单，不可作废");
        }
        TMDemandOrder tmDemandOrder = demandOrderService.getById(id);
        tmDemandOrder.setDemandStatus(DemandStatusEnum.ABOLISHED.value());
        tmDemandOrder.setReasonForVoiding(reasonForVoiding);
        //删除运输结算单
        List<TMStatementOrder> list = statementOrderService.lambdaQuery().eq(TMStatementOrder::getParentId, tmDemandOrder.getId()).list();
        if (!list.isEmpty()) {
            statementOrderService.deleteLogic(list.stream().map(TMStatementOrder::getId).collect(Collectors.toList()));
        }
        WaybillEventDTO waybillEventDTO = new WaybillEventDTO();
        org.springblade.core.tool.utils.BeanUtil.copyNonNull(tmDemandOrder, waybillEventDTO);
        waybillEventDTO.setType(SapTransportActionEnum.DEMAND.value());
        waybillEventDTO.setEventTime(LocalDateTime.now());
        waybillEventDTO.setCancel("x");
        waybillEventDTO.setTorId(tmDemandOrder.getOrderNo());
        sapRequestService.pushTmForUpdateWaybill(waybillEventDTO);
        return R.status(demandOrderService.updateById(tmDemandOrder));
    }

}
